/*
 * (C) Copyright 2012
 *
 *
 */

#include "stdio.h"
#include "stdlib.h"
#include "usb.h"
#include "part.h"

#define USB_CMD_DEBUG

#ifdef	USB_CMD_DEBUG
#define USB_CMD_PRINTF(s)				printf(s);							
#define USB_CMD_PRINTFHEX(p, n, s)		{printf(p);printf("0x%x",n);printf(s);}
#define USB_CMD_PRINTFNUM(p, n, s)	    {printf(p);printf("%d",n);printf(s);}
#else
#define USB_CMD_PRINTF(s)												
#define USB_CMD_PRINTFHEX(p, n, s)		
#define USB_CMD_PRINTFNUM(p, n, s)	    
#endif


extern block_dev_desc_t *usb_stor_get_dev(int index);
/* some display routines (info command) */
char *usb_get_class_desc(unsigned char dclass)
{
	switch (dclass) {
	case USB_CLASS_PER_INTERFACE:
		return "See Interface";
	case USB_CLASS_AUDIO:
		return "Audio";
	case USB_CLASS_COMM:
		return "Communication";
	case USB_CLASS_HID:
		return "Human Interface";
	case USB_CLASS_PRINTER:
		return "Printer";
	case USB_CLASS_MASS_STORAGE:
		return "Mass Storage";
	case USB_CLASS_HUB:
		return "Hub";
	case USB_CLASS_DATA:
		return "CDC Data";
	case USB_CLASS_VENDOR_SPEC:
		return "Vendor specific";
	default:
		return "";
	}
}

void usb_display_class_sub(unsigned char dclass, unsigned char subclass,
			   unsigned char proto)
{
	switch (dclass) {
	case USB_CLASS_PER_INTERFACE:
		USB_CMD_PRINTF("See Interface");
		break;
	case USB_CLASS_HID:
		USB_CMD_PRINTF("Human Interface, Subclass: ");
		switch (subclass) {
		case USB_SUB_HID_NONE:
			USB_CMD_PRINTF("None");
			break;
		case USB_SUB_HID_BOOT:
			USB_CMD_PRINTF("Boot ");
			switch (proto) {
			case USB_PROT_HID_NONE:
				USB_CMD_PRINTF("None");
				break;
			case USB_PROT_HID_KEYBOARD:
				USB_CMD_PRINTF("Keyboard");
				break;
			case USB_PROT_HID_MOUSE:
				USB_CMD_PRINTF("Mouse");
				break;
			default:
				USB_CMD_PRINTF("reserved");
				break;
			}
			break;
		default:
			USB_CMD_PRINTF("reserved");
			break;
		}
		break;
	case USB_CLASS_MASS_STORAGE:
		USB_CMD_PRINTF("Mass Storage, ");
		switch (subclass) {
		case US_SC_RBC:
			USB_CMD_PRINTF("RBC ");
			break;
		case US_SC_8020:
			USB_CMD_PRINTF("SFF-8020i (ATAPI)");
			break;
		case US_SC_QIC:
			USB_CMD_PRINTF("QIC-157 (Tape)");
			break;
		case US_SC_UFI:
			USB_CMD_PRINTF("UFI");
			break;
		case US_SC_8070:
			USB_CMD_PRINTF("SFF-8070");
			break;
		case US_SC_SCSI:
			USB_CMD_PRINTF("Transp. SCSI");
			break;
		default:
			USB_CMD_PRINTF("reserved");
			break;
		}
		USB_CMD_PRINTF(", ");
		switch (proto) {
		case US_PR_CB:
			USB_CMD_PRINTF("Command/Bulk");
			break;
		case US_PR_CBI:
			USB_CMD_PRINTF("Command/Bulk/Int");
			break;
		case US_PR_BULK:
			USB_CMD_PRINTF("Bulk only");
			break;
		default:
			USB_CMD_PRINTF("reserved");
			break;
		}
		break;
	default:
		USB_CMD_PRINTF(usb_get_class_desc(dclass));
		break;
	}
}

void usb_display_string(struct usb_device *dev, int index)
{
	char buffer[256];
	if (index != 0) {
		if (usb_string(dev, index, &buffer[0], 256) > 0)
			;
	}
}

void usb_display_desc(struct usb_device *dev)
{
	if (dev->descriptor.bDescriptorType == USB_DT_DEVICE) {
		USB_CMD_PRINTFNUM(" ",dev->devnum," ");
		USB_CMD_PRINTF(usb_get_class_desc(dev->config.if_desc[0].desc.bInterfaceClass));
		USB_CMD_PRINTFHEX("USB Revision ", (dev->descriptor.bcdUSB>>8) & 0xff, " ");
		USB_CMD_PRINTFHEX(" ", dev->descriptor.bcdUSB & 0xff, "\r\n");
				
		if (strlen(dev->mf) || strlen(dev->prod) ||
		    strlen(dev->serial))
			{
			USB_CMD_PRINTF(" - ");
			USB_CMD_PRINTF(dev->mf);
			USB_CMD_PRINTF(" ");
			USB_CMD_PRINTF(dev->prod);
			USB_CMD_PRINTF(" ");
			USB_CMD_PRINTF(dev->serial);
			USB_CMD_PRINTF(" \r\n");
			}
		if (dev->descriptor.bDeviceClass) {
			USB_CMD_PRINTF(" - Class: ")
			
			usb_display_class_sub(dev->descriptor.bDeviceClass,
					      dev->descriptor.bDeviceSubClass,
					      dev->descriptor.bDeviceProtocol);
			USB_CMD_PRINTF(" \r\n");
		} else {
			USB_CMD_PRINTF(" - Class: (from Interface) ");
			USB_CMD_PRINTF(usb_get_class_desc(dev->config.if_desc[0].desc.bInterfaceClass));
		}
		USB_CMD_PRINTFNUM(" - PacketSize:  ", dev->descriptor.bMaxPacketSize0, " ");
		USB_CMD_PRINTFNUM(" Configurations:  ", dev->descriptor.bNumConfigurations, " \r\n");
		USB_CMD_PRINTFHEX(" - Vendor:   ", dev->descriptor.idVendor, " ");
		USB_CMD_PRINTFHEX("  Product    ", dev->descriptor.idProduct, " ");
		USB_CMD_PRINTFNUM("  Version     ", (dev->descriptor.bcdDevice>>8) & 0xff, "");
		USB_CMD_PRINTFNUM(".", dev->descriptor.bcdDevice & 0xff, " \r\n");
	}

}

void usb_display_conf_desc(struct usb_configuration_descriptor *config,
			   struct usb_device *dev)
{
	USB_CMD_PRINTFNUM("   Configuration: ", config->bConfigurationValue, " \r\n");
	USB_CMD_PRINTFNUM(" - Interfaces:  ", config->bNumInterfaces, " ");
	USB_CMD_PRINTF((config->bmAttributes & 0x40) ? "Self Powered " : "Bus Powered ");
	USB_CMD_PRINTF((config->bmAttributes & 0x20) ? "Remote Wakeup " : "");
	USB_CMD_PRINTFNUM(" mA", config->bMaxPower*2, " \r\n")
	
	if (config->iConfiguration) {
		USB_CMD_PRINTF("   - ");
		usb_display_string(dev, config->iConfiguration);
		USB_CMD_PRINTF("\n");
	}
}

void usb_display_if_desc(struct usb_interface_descriptor *ifdesc,
			 struct usb_device *dev)
{
	USB_CMD_PRINTFNUM("   Interface: ", ifdesc->bInterfaceNumber, " \r\n");
	USB_CMD_PRINTFNUM(" - Alternate Setting ", ifdesc->bAlternateSetting,"\r\n");
	USB_CMD_PRINTFNUM("   Endpoints: ", ifdesc->bNumEndpoints,"\r\n");
	USB_CMD_PRINTF(" - Class ");
	usb_display_class_sub(ifdesc->bInterfaceClass,
		ifdesc->bInterfaceSubClass, ifdesc->bInterfaceProtocol);
	USB_CMD_PRINTF("\r\n");
	if (ifdesc->iInterface) {
		USB_CMD_PRINTF(" - ");
		usb_display_string(dev, ifdesc->iInterface);
		USB_CMD_PRINTF("\r\n");
	}
}

void usb_display_ep_desc(struct usb_endpoint_descriptor *epdesc)
{
	USB_CMD_PRINTFNUM(" - Endpoint ", epdesc->bEndpointAddress & 0xf, " ");
	USB_CMD_PRINTF((epdesc->bEndpointAddress & 0x80) ? "In" : "Out");

	switch ((epdesc->bmAttributes & 0x03)) {
	case 0:
		USB_CMD_PRINTF("Control");
		break;
	case 1:
		USB_CMD_PRINTF("Isochronous");
		break;
	case 2:
		USB_CMD_PRINTF("Bulk");
		break;
	case 3:
		USB_CMD_PRINTF("Interrupt");
		break;
	}
	USB_CMD_PRINTFNUM(" MaxPacket ", epdesc->wMaxPacketSize," ");
	if ((epdesc->bmAttributes & 0x03) == 0x3)
		USB_CMD_PRINTFNUM(" Interval ", epdesc->bInterval, " ms");
	USB_CMD_PRINTF("\r\n");
}

/* main routine to diasplay the configs, interfaces and endpoints */
void usb_display_config(struct usb_device *dev)
{
	struct usb_config *config;
	struct usb_interface *ifdesc;
	struct usb_endpoint_descriptor *epdesc;
	int i, ii;

	config = &dev->config;
	usb_display_conf_desc(&config->desc, dev);
	for (i = 0; i < config->no_of_if; i++) {
		ifdesc = &config->if_desc[i];
		usb_display_if_desc(&ifdesc->desc, dev);
		for (ii = 0; ii < ifdesc->no_of_ep; ii++) {
			epdesc = &ifdesc->ep_desc[ii];
			usb_display_ep_desc(epdesc);
		}
	}
	USB_CMD_PRINTF("\r\n");
}

/******************************************************************************
 * usb command intepreter
 */

int do_usb_inf()
{
	struct usb_device *dev = NULL;
	
		int d;
			for (d = 0; d < USB_MAX_DEVICE; d++) {
				dev = usb_get_dev_index(d);
				if (dev == NULL)
					break;
				usb_display_desc(dev);
				usb_display_config(dev);
			}
	
		return 0;
	
}

#if 0
static int usb_stor_curr_dev = -1; /* current device */
/* shows the device tree recursively */
void usb_show_tree_graph(struct usb_device *dev, char *pre)
{
	int i, index;
	int has_child, last_child;

	index = strlen(pre);
	
	/* check if the device has connected children */
	has_child = 0;
	for (i = 0; i < dev->maxchild; i++) {
		if (dev->children[i] != NULL)
			has_child = 1;
	}
	/* check if we are the last one */
	last_child = 1;
	if (dev->parent != NULL) {
		for (i = 0; i < dev->parent->maxchild; i++) {
			/* search for children */
			if (dev->parent->children[i] == dev) {
				/* found our pointer, see if we have a
				 * little sister
				 */
			//	port = i;
				while (i++ < dev->parent->maxchild) {
					if (dev->parent->children[i] != NULL) {
						/* found a sister */
						last_child = 0;
						break;
					} /* if */
				} /* while */
			} /* device found */
		} /* for all children of the parent */
		
		/* correct last child */
		if (last_child)
			pre[index-1] = ' ';
	} /* if not root hub */
	else
		{
			;
		}
	pre[index++] = ' ';
	pre[index++] = has_child ? '|' : ' ';
	pre[index] = 0;
	
	if (strlen(dev->mf) || strlen(dev->prod) || strlen(dev->serial))
		{
			;
		}
		
	if (dev->maxchild > 0) {
		for (i = 0; i < dev->maxchild; i++) {
			if (dev->children[i] != NULL) {
				usb_show_tree_graph(dev->children[i], pre);
				pre[index] = 0;
			}
		}
	}
}

/* main routine for the tree command */
void usb_show_tree(struct usb_device *dev)
{
	char preamble[32];

	memset(preamble, 0, 32);
	usb_show_tree_graph(dev, &preamble[0]);
}

int do_usb_reset()
{
	int i;
//reset
//start
		usb_stop();
		i = usb_scan_init();
		if (i >= 0) {
			/* try to recognize storage devices immediately */
			usb_stor_curr_dev = usb_stor_scan(1);
		}
		return 0;
}

int do_usb_stop()
{
//stop

	usb_stop();
	return 0;
}

int do_usb_tree()
{

		usb_show_tree(usb_get_dev_index(0));
		return 0;
}

int do_usb_stor()
{
		return usb_stor_info();
}

int do_usb_part()
{
		int devno, ok = 0;
		block_dev_desc_t *stor_dev;
		usb_stor_curr_dev = usb_stor_scan(1);
		for (devno = 0; ; ++devno) {
				stor_dev = usb_stor_get_dev(devno);
				if (stor_dev == NULL)
					break;
				if (stor_dev->type != DEV_TYPE_UNKNOWN) {
					ok++;
					if (devno)
						USB_CMD_PRINTF("\r\n");
					USB_CMD_PRINTFHEX("print_part of ", devno,"\r\n");
					print_part(stor_dev);
				}
			}
		return 0;
}

int do_usb_read()
{
	block_dev_desc_t *stor_dev;
	uint8_t *addr = 0;
	unsigned long blk  = 0;
	unsigned long cnt  = 1;
	unsigned long n;

	addr =(uint8_t *) malloc(512*cnt);
	memset(addr,0,512*cnt);
	usb_stor_curr_dev = usb_stor_scan(1);
	stor_dev = usb_stor_get_dev(usb_stor_curr_dev);
	n = stor_dev->block_read(usb_stor_curr_dev, blk, cnt,
						 (uint32_t *)addr);
	for(n=0;n<512*cnt;n++)
		{
			if(n%16 == 0)
				{
				USB_CMD_PRINTF("\r\n");
				USB_CMD_PRINTFHEX(" ",n,":  ");
				}
			USB_CMD_PRINTFHEX(" ", *(addr+n), " ");
			
		}
	return 0;


}

int do_usb_write()
{

	unsigned long addr = 0;
	unsigned long blk  = 0;
	unsigned long cnt  = 0;
	block_dev_desc_t *stor_dev;

	stor_dev = usb_stor_get_dev(usb_stor_curr_dev);
	stor_dev->block_write(usb_stor_curr_dev, blk, cnt,
						(uint32_t *)addr);
	return 0;
}

int do_usb_dev()
{
	usb_stor_get_dev(0);
	return 0;
}

#endif

